/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package MortgageCore;

import Household.Household;

import Bank.Bank;
import Bank.BankMarket;
import simframe.simulation.*;

/**
 *
 * @author karakots
 */
public class SavingsSim extends Simulation {

    public static final int kWorldWidth = 900; //1600;
    public static final int kWorldHeight = 600;

    public Swarm<Household> households = new Swarm<Household>();
    public Swarm<Bank> banks = new Swarm<Bank>();
    
    public Swarm<BankMarket> bankMarkets = new Swarm<BankMarket>();
    public BankMarket bankMarket;

    public Swarm<MortgageMarket> mortgageMarkets = new Swarm<MortgageMarket>();
    public MortgageMarket mortgageMarket;

    public Swarm<HouseMarket> houseMarkets = new Swarm<HouseMarket>();
    public HouseMarket houseMarket;

    public Swarm<JobMarket> jobMarkets = new Swarm<JobMarket>();
    public JobMarket jobMarket;


    // define actions as runnable methods
    static final long morning = 6 * Time.Hour;
    static final long noon = 12 * Time.Hour;
    static final long afternoon = 15 * Time.Hour;
    static final long evening = 18 * Time.Hour;
    static private long timeT;

    static final Time.Next daily = new Time.Next() {

        public long getNext(long t) {
           timeT = t + Time.Day;
           return timeT;
        }
    };

    public long getDay() {
        return Time.getDay(timeT);
    }

    public SavingsSim(int numHouseholds, int numBanks, boolean logData, boolean adaptiveBanks) {

        // create objects
        int ii;

        // create one mortgage market
        mortgageMarket = MortgageMarket.Get();
        mortgageMarkets.add(mortgageMarket);

        // create one bank market for handling bank faileres and M&A
        bankMarket = new BankMarket();
        bankMarkets.add(bankMarket);

        // create one house market
        try {
            houseMarket = new HouseMarket();
        } catch (java.io.IOException e) {
        } finally {
            houseMarkets.add(houseMarket);
        }

        // create one job market
        try {
            jobMarket = new JobMarket();
        } catch (java.io.IOException e) {
        } finally {
            jobMarkets.add(jobMarket);
        }

        double bankScaleFactor = 110000000 / numHouseholds;
        if (bankScaleFactor > 10000)
            bankScaleFactor = 10000;
        
        for (ii = 0; ii < numBanks; ++ii) {
            Bank bank = new Bank(adaptiveBanks);
            bank.setMortgageMarket(mortgageMarket);
            bank.setBankMarket(bankMarket);
            if (ii == 0)    // static variable, only need to set once
                bank.setBankScaleFactor(bankScaleFactor);
            bankMarket.addBank(bank);
            bank.setEnableLogData(logData);
            bank.setLogData(true);
            banks.add(bank);
        }

        // needs to be done after all the banks are created
        for (ii = 0; ii < numBanks; ++ii)
            banks.getAtIndex(ii).setUpFinances();

        Household.InitializeGenerator(numHouseholds);
        for (ii = 0; ii < numHouseholds; ++ii) {
            Household hh = new Household();
            Bank bank = bankMarket.selectSavingsBankBasedOnDeposits();
            //Bank bank = banks.selectRandom();
            hh.openAccount(bank);
            hh.hb().setMortgageMarket(mortgageMarket);
            hh.hb().setHouseMarket(houseMarket);
            hh.setJobMarket(jobMarket);
            hh.initializeHomeStatus();
            hh.hb().setEnableLogData(logData);
            hh.hb().setLogData(true);
            households.add(hh);
        }

        // create simulation threads
        // house market thread
        newThread().addSimObject(houseMarkets);
        houseMarkets.addInitSwarmAction(HouseMarket.newDay, morning, daily);

        // job market thread
        newThread().addSimObject(jobMarkets);
        jobMarkets.addInitSwarmAction(JobMarket.newDay, morning, daily);

        // mortgage market thread
        newThread().addSimObject(mortgageMarkets);
        // mortgage market actions
        mortgageMarkets.addInitSwarmAction(MortgageMarket.ageOffers, evening, daily);
        mortgageMarkets.addInitSwarmAction(MortgageMarket.tryToFillRequests, afternoon, daily);

        // Household thread
        newThread().addSimObject(households);
        // Household actions
        households.addInitSwarmAction(Household.newDay, morning, daily);
        households.addInitSwarmAction(Household.considerHousingAction, noon, daily);
        households.addInitSwarmAction(Household.work, noon, daily);
        households.addInitSwarmAction(Household.payExpenses, afternoon, daily);
        households.addInitSwarmAction(Household.manageCash, evening, daily);

        // Bank Thread
        newThread().addSimObject(banks);
        // add bank actions
        banks.addInitSwarmAction(Bank.newDay, morning, daily);
        banks.addInitSwarmAction(Bank.offerMortgages, noon, daily);
        banks.addInitSwarmAction(Bank.computeMonthlyInterest, noon, daily);
        banks.addInitSwarmAction(Bank.agePreapprovedMortgages, evening, daily);

        //
        // User Interface
        createParameters();
    }


    // These should be replaced by
    // more generic parameter changing methods
    
    public synchronized void updateSavingsRateScale(double val) {
        for (Household hh : households) {
            hh.updateSavingsRateScale(val);
        }
    }

    public synchronized void updateRateSpreadScale(double val) {
        for (Bank bank : banks) {
            bank.updateRateSpreadScale(val);
        }
    }

  


    private void createParameters() {
        // update savings

        String savingsDesc = "This adjusts the interest rate on home loans";
        SimParameter savingRate = new SimParameter("interest rate", savingsDesc, "Loans",
                new SimParameter.Method() {
            public void update(double val) {
                updateRateSpreadScale(val);
            }
        });

        savingRate.setRange(0, 2);
        this.addParameter(savingRate);

        String intDesc = "This adjusts the rate at which households save their earnings";
           SimParameter interestRate = new SimParameter("savings rate", intDesc, "Savings",
                new SimParameter.Method() {
            public void update(double val) {
                updateSavingsRateScale(val);
            }
        });

        interestRate.setRange(0, 2);
        this.addParameter(interestRate);
    }

}
